/*
 * Executor.cpp
 *
 *  Created on: Jun 15, 2010
 *      Author: administrator
 */
#include "types/common.h"
#include "modules/Executor.h"
#include "types/query.h"
#include "types/hash.h"

using namespace std;
HashMap<string,table_info,Executor> catalog;
HashMap<string,table_index_info,Executor> index_catalog;
map<string,string> index_table_map;

Executor::Executor() {
}

Executor::~Executor() {
}

void printTables(string table_name, table_info table)
{
	printf("\n%s",table_name.c_str());
	printf("          ");
	for(int i=0;i<table.num_attr;i++)
		printf("  %s",table.attributes[i].c_str());
	printf("\n");
}

void removeEntries(string table_name, table_index_info index)
{
	index_catalog.remove(table_name);
}

void Executor::write(SQL *sql)
{
	outCatalogFile = fopen("dbs/catalog.txt", "a+a");
	char *numberAttributes = new char[1];
	table_info table;

	snprintf(numberAttributes,sizeof(sql->num_attr), "%d", sql->num_attr);

	if(outCatalogFile){

		fprintf(outCatalogFile,"%s",sql->table_name.c_str());
		table.tableName = sql->table_name;
		fprintf(outCatalogFile,"%c",' ');
		fprintf(outCatalogFile,numberAttributes);
		table.num_attr = sql->num_attr;
		fprintf(outCatalogFile,"%c",' ');
		for(int i=0;i<sql->num_attr;i++)
		{
			fprintf(outCatalogFile,"%s",sql->attributes[i].c_str());
			table.attributes.push_back(sql->attributes[i]);
			fprintf(outCatalogFile,"%c",' ');
		}
		fprintf(outCatalogFile,"%c",'\n');
	}
	fclose(outCatalogFile);

	catalog.insert(table.tableName,table);
	table.attributes.clear();
}

void Executor::writeIndex(SQL *sql)
{
	outCatalogFile = fopen("dbs/catalog_index.txt", "a+a");

	if(outCatalogFile){

		fprintf(outCatalogFile,"%s",sql->table_name.c_str());
		fprintf(outCatalogFile,"%c",' ');
		fprintf(outCatalogFile,"%s",sql->index_name.c_str());
		fprintf(outCatalogFile,"%c",' ');
		if(sql->index_duplicate){
			fprintf(outCatalogFile,"%s","1");
		}
		else{
			fprintf(outCatalogFile,"%s","0");
		}
		fprintf(outCatalogFile,"%c",' ');

		fprintf(outCatalogFile,"%s",sql->attributes[0].c_str());
		fprintf(outCatalogFile,"%c",' ');

		fprintf(outCatalogFile,"%c",'\n');
	}
	fclose(outCatalogFile);

	table_index_info tempIndex;
	index_table_map[sql->index_name] = sql->table_name;
	try{
		tempIndex = index_catalog.lookup(sql->table_name);
		index_catalog.remove(sql->table_name);
		tempIndex.indexName.push_back(sql->index_name);
		tempIndex.attributeName.push_back(sql->attributes[0]);
		if(sql->index_duplicate)
			tempIndex.index_duplicate.push_back(true);
		else
			tempIndex.index_duplicate.push_back(false);
		index_catalog.insert(tempIndex.tableName,tempIndex);
	}
	catch(...){
		tempIndex.indexName.push_back(sql->index_name);
		tempIndex.attributeName.push_back(sql->attributes[0]);
		if(sql->index_duplicate)
			tempIndex.index_duplicate.push_back(true);
		else
			tempIndex.index_duplicate.push_back(false);
		index_catalog.insert(tempIndex.tableName,tempIndex);
	}
	tempIndex.indexName.clear();
	tempIndex.attributeName.clear();
	tempIndex.index_duplicate.clear();
}

bool Executor::isequal(string s1,string s2)
{
	return (s1 == s2);
}

void Executor::read()
{
	table_info table;
	outCatalogFile = fopen("dbs/catalog.txt", "r");
	char temp[100] = {0, };
	char tempTableName[4] = {0, };
	char tempNum[2] = {0, };
	char tempAttName[4] = {0, };
	if(!outCatalogFile)
	        return;

	if(outCatalogFile)
	{
		//fgets(temp,100,outCatalogFile);
		while(fgets(temp,100,outCatalogFile) != NULL){
			int i=0;

			while(temp[i] != ' '){
				tempTableName[i] = temp[i];
				i++;
			}
			table.tableName.assign(tempTableName);
			i++;
			for(int j=0;temp[i] != ' ';j++){
				tempNum[j] = temp[i];
				i++;
			}
			table.num_attr = atoi(tempNum);
			i++;
			for(int j=0;temp[i] != '\n';j++)
			{
				if(temp[i] != ' ')
					tempAttName[j] = temp[i];
				if(temp[i] == ' '){
					j++;
					tempAttName[j] = '\0';
					table.attributes.push_back(tempAttName);
					j=-1;
				}
				i++;
			}
			catalog.insert(table.tableName,table);
			table.attributes.clear();
		}
	}
	fclose(outCatalogFile);
}

void Executor::readIndex()
{
	table_index_info index,tempIndex;
	char temp[100] = {0, };
	char tempTableName[4] = {0, };
	char tempIndexName[4] = {0, };
	char tempDuplicate[1] = {0};
	char tempAttName[4] = {0, };

	index_catalog.foreach(removeEntries);
	index_table_map.clear();

	outCatalogFile = fopen("dbs/catalog_index.txt", "r");

	if(!outCatalogFile)
		return;

	if(outCatalogFile)
	{
		while(fgets(temp,100,outCatalogFile) != NULL){
			int i=0;

			for(int j=0;temp[i] != ' ';j++){
				tempTableName[j] = temp[i];
				i++;
			}
			index.tableName.assign(tempTableName);
			i++;

			for(int j=0;temp[i] != ' ';j++){
				tempIndexName[j] = temp[i];
				i++;
			}
			//index.indexName.assign(tempIndexName);
			i++;

			tempDuplicate[0] = temp[i];
//			if(tempDuplicate[0] == '1')
//				index.index_duplicate = true;
//			else
//				index.index_duplicate = false;
			i = i+2;

			for(int j=0;temp[i] != ' ';j++){
				tempAttName[j] = temp[i];
				i++;
			}
			//index.attributeName.assign(tempAttName);
			string indexName = "";
			indexName.assign(tempIndexName);
			index_table_map[tempIndexName] = index.tableName;
			try{
				tempIndex = index_catalog.lookup(index.tableName);
				index_catalog.remove(index.tableName);
				tempIndex.indexName.push_back(tempIndexName);
				tempIndex.attributeName.push_back(tempAttName);
				if(tempDuplicate[0] == '1')
					tempIndex.index_duplicate.push_back(true);
				else
					tempIndex.index_duplicate.push_back(false);
				index_catalog.insert(tempIndex.tableName,tempIndex);
			}
			catch(...){
				index.indexName.push_back(tempIndexName);
				index.attributeName.push_back(tempAttName);
				if(tempDuplicate[0] == '1')
					index.index_duplicate.push_back(true);
				else
					index.index_duplicate.push_back(false);
				index_catalog.insert(index.tableName,index);
				index.indexName.clear();
				index.attributeName.clear();
				index.index_duplicate.clear();
			}
		}
	}
}

bool Executor::validateCreateTable(SQL *sql)
{
	table_info table;
	try{
		table = catalog.lookup(sql->table_name);
		cout<<"Error: Table already present." << endl;
	}
	catch(...){
		write(sql);
		return true;
	}
	return false;
}

bool Executor::validateCreateIndex(SQL *sql)
{
	table_info table;
	table_index_info index;

	bool attributepresent = false;

	try{
		table = catalog.lookup(sql->table_name);
		for(int i=0; i<table.num_attr;i++){
			if(sql->attributes[0] == table.attributes[i]){
				attributepresent = true;
				break;
			}
		}
		if(!attributepresent){
			cout<< "Error: index attribute " << sql->attributes[0] << " not present." << endl;
			return false;
		}
	}
	catch(...){
		cout<<"Error: Table " << sql->table_name << " not present." << endl;
		return false;
	}
	if(index_table_map.find(sql->index_name) == index_table_map.end()){
		try{
			index = index_catalog.lookup(sql->table_name);
			if(index.indexName.size() == 2){
				cout<<"Error: table " << sql->table_name << " already have 2 indices." << endl;
				return false;
			}
			return true;
		}
		catch(...){

			return true;
		}
	}
	else{
		cout<<"Error: Index " << sql->index_name << " already present." << endl;
	}

	return false;
}

bool Executor::validateInsert(SQL *sql)
{
	table_info table;
	try{
		table = catalog.lookup(sql->table_name);
		if(sql->num_values != table.num_attr){
			cout<< "Error: number of attribute values are not correct." << endl;
			return false;
		}
		return true;
	}
	catch(...){
		cout<<"Error: Table not present." << endl;
	}
	return false;
}

bool Executor::validateSelect(SQL *sql)
{
	table_info table;
	try{
		table = catalog.lookup(sql->table_name);
		if(sql->num_attr == 0){
			return validateWhere(&table,sql);
		}
		else{
			for(int i=0;i<sql->num_attr;i++)
			{
				for(int j=0;i<table.num_attr;j++){
					if(sql->attributes[i] == table.attributes[j]){
						//cout<<"Attribute present."<<endl;
						break;
					}
					else if(j == table.num_attr-1){
						cout<<"Error: attribute to select not found." << endl;
						return false;
					}
				}
			}
			return validateWhere(&table,sql);
		}
	}
	catch(...){
		cout<<"Error: Table not present." << endl;
	}
	return false;
}

bool Executor::validateWhere(table_info *table, SQL *sql){
	
	RecordManager recmanager = RecordManager();
	map<string,string>::iterator it;
	vector<Record> records1,records2,results;
	Condition con1,con2[sql->conditions.size()];
	int j =0;

	if(sql->conditions.empty()){
		records1 = recmanager.read_records(sql->table_name);
		printRecords(records1,table,sql);
	}
	else{
		for(it=sql->conditions.begin(); it != sql->conditions.end(); it++){
			for(int i=0;i<table->num_attr;i++){
				if((*it).first == table->attributes[i]){
					//cout<<"Attribute present."<<endl;
					if(sql->conditions.size()==1){
						con1.lvalue = (UINT32)i;
						break;
					}
					else if(sql->conditions.size() > 1){
						con2[j].lvalue = (UINT32)i;
						break;
					}
				}
				else if(i == (table->num_attr-1)){
					cout<<"Error: attribute in the condition not found." << endl;
					return false;
				}
			}
			for(int i=0;i<table->num_attr;i++){
				if((*it).second == table->attributes[i]){
					//cout<<"Second Attribute present."<<endl;
					if(sql->conditions.size()==1){
						con1.type = attr_check;
						con1.rvalue = (UINT32)i;
						con1.op = (sql->ope[0] == "=")?eq : (sql->ope[0] == ">")?gt : (sql->ope[0] == "<")?lt : (sql->ope[0] == ">=")?ge : (sql->ope[0] == "<=")?le : ne;
						break;
					}
					else if(sql->conditions.size() > 1){
						con2[j].type = attr_check;
						con2[j].rvalue = (UINT32)i;
						con2[j].op = (sql->ope[j] == "=")?eq : (sql->ope[j] == ">")?gt : (sql->ope[j] == "<")?lt : (sql->ope[j] == ">=")?ge : (sql->ope[j] == "<=")?le : ne;
						j++;
						break;
					}
				}
				else if(i == (table->num_attr-1)){
					//cout<<"Value is present."<<endl;
					if(sql->conditions.size()==1){
						con1.type = val_check;
						int rvalue = atoi((*it).second.c_str());
						con1.rvalue = (UINT32)rvalue;
						con1.op = (sql->ope[0] == "=")?eq : (sql->ope[0] == ">")?gt : (sql->ope[0] == "<")?lt : (sql->ope[0] == ">=")?ge : (sql->ope[0] == "<=")?le : ne;
						break;
					}
					else if(sql->conditions.size() > 1){
						con2[j].type = val_check;
						int rvalue = atoi((*it).second.c_str());
						con2[j].rvalue = (UINT32)rvalue;
						con2[j].op = (sql->ope[j] == "=")?eq : (sql->ope[j] == ">")?gt : (sql->ope[j] == "<")?lt : (sql->ope[j] == ">=")?ge : (sql->ope[j] == "<=")?le : ne;
						j++;
						break;
					}
				}
			}
		}
		if(sql->conditions.size() == 1){
			records1 = recmanager.read_records(sql->table_name,con1);
			printRecords(records1,table,sql);
		}
		else if(sql->conditions.size() > 1 && sql->andCondition){
			records1 = recmanager.read_records(sql->table_name,con2[0]);
			records2 = recmanager.read_records(sql->table_name,con2[1]);
			results = recmanager.common_records(records1,records2);
			printRecords(results,table,sql);
		}
		else if(sql->conditions.size() > 1 && sql->orCondition){
			records1 = recmanager.read_records(sql->table_name,con2[0]);
			records2 = recmanager.read_records(sql->table_name,con2[1]);
			results = recmanager.merge_records(records1,records2);
			printRecords(results,table,sql);
		}
	}
	return true;
}

void Executor::printCatalog()
{
	Executor ex;
	table_info table;
	string table_name;
	printf("\nCatalog contents:\n\n");
	printf("---------------------------------------\n");
	printf("TABLE-NAME    ATTRIBUTES\n");
	printf("---------------------------------------\n");
	catalog.foreach(printTables);
}

void Executor::printRecords(vector<Record> &records, table_info *table, SQL *sql)
{
	vector<int> attr;
	printf("\n");
	if(sql->num_attr == 0){
		for(int i=0;i<table->num_attr;i++){
			printf("%s    ",table->attributes[i].c_str());
		}
		printf("\n---------------------------------\n");
		for(unsigned int i=0; i<records.size();i++){
			for(int j=0;j<table->num_attr;j++)
				printf("%d      ",records[i].attribute(j));
			printf("\n");
		}
	}
	else if(sql->num_attr >= 1)
	{
		for(int i=0;i<table->num_attr;i++)
			for(int j=0;j<sql->num_attr;j++)
				if(sql->attributes[j] == table->attributes[i])
					attr.push_back(i);
		for(int i=0;i<sql->num_attr;i++){
			printf("%s      ",sql->attributes[i].c_str());
		}
		printf("\n----------------------------\n");
		for(unsigned int i=0; i<records.size();i++){
			for(int j=0;j<attr.size();j++){
					printf("%d        ",records[i].attribute(attr[j]));
			}
			printf("\n");
		}
	}
}
